/* eslint-disable @typescript-eslint/ban-types */
import { isEqual, matches, uniqWith } from 'lodash'

import { BatchEvent } from '@slardar/sdk-template'

import { createChainInstance } from './utils'

/**
 * 反复执行一组操作，保证多次执行上报数据不变
 * @param options.opList 需要多次执行的一组操作
 * @param options.step 每次收集数据间隔的操作执行组数
 * @param options.opMaxCount 执行操作的总组数，相当于 for (i=0; i<opMaxCount; i+=step)
 * @param options.matchObj 期望匹配的对象
 * @param options.matchCount 期望组操作执行后，匹配对象出现的次数，小于或大于都将失败，为-1时不做校验
 * @param options.timeout 等待每个请求的最大窗口期
 * @param options.maxTryCount 最多需判断的请求数（针对单组 operation 操作）
 * @param options.equalProps 期望每个匹配到的对象的哪些属性内容需要完全相等
 * @return 所有匹配到的对象
 */
export function repeatAndAssert(options: {
  opList: Function[]
  matchObj: any
  opMaxCount: number
  step?: number
  matchCount?: number
  timeout?: number
  maxTryCount?: number
  equalProps?: string[]
  onSuccess?: (matchObjs: any[]) => void
}) {
  const defaultOptions = {
    step: 1,
    matchCount: -1,
    timeout: 2000,
    maxTryCount: Infinity,
    equalProps: [] as string[],
  }
  const { opList, step, opMaxCount, matchObj, matchCount, timeout, maxTryCount, equalProps, onSuccess } = {
    ...defaultOptions,
    ...options,
  }

  let opCount = 0
  const matchObjs: any[] = []

  function repeat() {
    let _step = step
    while (opCount < opMaxCount && _step--) {
      opCount++
      opList.forEach((o) => o())
    }
  }

  function handleOpRepeatDone() {
    if (matchObjs.length === matchCount) {
      // 校验属性全等
      if (equalProps!.length > 0) {
        // 深对比去重
        const uniqObjList = uniqWith(matchObjs, isEqual)
        // 只有去重后只剩下一条数据才符合预期
        expect(uniqObjList.length, `expect all props picked is equal`).equal(1)
      }
      expect(true).true
      onSuccess?.(matchObjs)
    } else {
      expect(false, `expect match objs count is ${matchCount}`).true
    }
  }
  createChainInstance<BatchEvent<any>>({
    getChainInstance: () => {
      // 在每次创建实例前 都重复执行操作
      repeat()
      return cy.wait('@post', { timeout }).its('request.body')
    },
    maxTryCount,
    handler: (body) => {
      if (body.list) {
        matchObjs.push(...body.list.filter(matches(matchObj)))
        // 校验匹配数量，如果严格限制了 matchCount，那么超出数量的可以提前断言报错
        if (matchCount !== -1 && matchObjs.length > matchCount) {
          expect(false, `expect match objs count is ${matchCount}`).true
        }
        // 终止条件为操作组数到达上限
        if (opCount === opMaxCount) {
          return true
        }
      }
      return false
    },
    onMaxCount: () => expect(false, 'should not reach here, try to assign a greater value to maxTryCount'),
    onSuccess: handleOpRepeatDone,
  })
}
